package ibm.cognos.imt

import groovy.xml.MarkupBuilder
import groovy.xml.XmlUtil
import ibm.cognos.imt.chart.CombinationChart
import ibm.cognos.imt.chart.PieChart
import ibm.cognos.imt.chart.V2BubbleChart
import ibm.cognos.imt.dataUsage.DataItemWrapper
import ibm.cognos.imt.template.*

/**
 * Created with IntelliJ IDEA.
 * User: eric
 * Date: 12/30/13
 * Time: 4:15 PM
 * To change this template use File | Settings | File Templates.
 */
class CogReport {


    def elementTypes = ['crosstab', 'pieChart', 'list', 'bubbleChart']

    String specification = null
    def rootNode = null
    String modelPath

    def writer
    def builder

    int elementCounter = 1

    def java.util.List dataItemList = new ArrayList()
    def java.util.List filterList = new ArrayList()

    def layoutsSpecStr

    HashMap elementMap = [:]
    HashMap queryMap = [:]

    def root

    CogReport(String reportSpec) {
//        specification = reportSpec
//        println(reportSpec)

        rootNode = new XmlSlurper(false, false).parseText(reportSpec)
        def elements = rootNode.layouts.layout.reportPages.page.pageBody.contents

//        rootNode = new XmlParser().parseText(reportSpec)
//        def contents = rootNode.layouts[0].layout[0].reportPages[0].page[0].pageBody[0].contents[0]
        searchForElements(elements)

    }

    def list(Collection array, Closure closure = null) {
        def items = array.collect {it.getDataItem()}
        def list = new List(this, * array)
        def queryName = "Query" + (queryMap.size() + 1)
        def query = new Query(this, queryName, items)
        queryMap << [(queryName): query]
        list.queryName = queryName
        list.template = new ListTemplate(list)
        if (closure != null) {
            def slurper = list.getDomNode()
//            println slurper
//            closure.delegate = slurper
            closure.call(slurper)
        }

        list
    }

    CogReport modelPath(String path) {
        modelPath = path
        this
    }

    String getXmlns() {
        String rootElementName = rootNode.name().toString()
        String ns = rootElementName.substring(rootElementName.indexOf("{") + 1, rootElementName.indexOf("}"))
        return "xmlns=\"$ns\" "
    }

    String getFormattedXml(node) {
        StringWriter sw = new StringWriter()
//    new XmlNodePrinter(new PrintWriter(sw)).print(node.layouts[0])
        new XmlNodePrinter(new PrintWriter(sw)).print(node)
        String xml = sw.toString()
        if (rootNode != node) {
            String updated = xml.replace(getXmlns(), "")
            return updated
        }
        return xml
    }

    def buildModel(node) {
//    ct = new ibm.cognos.imt.Crosstab(node)
//    println node.@refQuery
//    ct.queryName = node.@refQuery

//        node.'*'.each {
//            println "\t${it.name()}"
//        }

        if (node.name() == "crossTab") {
            return new Crosstab(node)
        }
        if (node.name() == "pieChart") {
            return new PieChart(node)
        }
        if (node.name() == "list") {
            return new List(node)
        }

        return null
    }

    def searchForElements(node) {
        node.breadthFirst().findAll { element ->
            elementTypes.each { type ->
                if (element.name() == type) {
                    def model = buildModel(element)
//                    println(model)
                    if (model != null) {
//                        println(element.name())
//                        println(elementMap)
                        elementMap.put(model.name, model)
//                        println(elementMap)
                    }
                }
            }
        }
    }

    CogReport() {
//        println("constructor of ibm.cognos.imt.CogReport")
        this.writer = new StringWriter()
        this.builder = new MarkupBuilder(writer)
        this.builder.setDoubleQuotes(true)
    }

    def table(rowCount, columnCount, closure) {
        def tab = new Table(rowCount, columnCount)
//        elementList<<tab
//        tab
//        eval(closure)
        if (closure) {
            closure.delegate = tab
//            closure.owner = tab
            closure.call()
        }
        tab.template = new TableTemplate(tab)
        tab
    }

//    def abstract desc()
    def desc() {
        throw new RuntimeException("need to overwrite desc() metod")
    }

    def getName() {
        if (name == null) {
            name = newName()
        }
        return name
    }

    def newName() {
        return "Element${elementCounter++}"
    }

    def crosstab(DataItemWrapper... wrappers) {
        def items = wrappers.collect {
            it.getDataItem()
        }
        def ct = new Crosstab(this, wrappers)
        def queryName = "Query" + (queryMap.size() + 1)
        def query = new Query(this, queryName, items)
        queryMap << [(queryName): query]
        ct.queryName = queryName
        ct
    }

    def PieChart pieChart(items, closure = null) {

    }

    def PieChart pieChart(DataItemWrapper... wrappers) {
        def items = wrappers.collect {
            it.getDataItem()
        }
//        println items.class
        def pie = new PieChart(this, wrappers)
        def queryName = "Query" + (queryMap.size() + 1)
        def query = new Query(this, queryName, items)
        queryMap << [queryname: query]
        pie.queryName = queryName
        pie.template = new PieChartTemplate(pie)
        pie
    }

    def combinationChart(DataItemWrapper... wrappers) {
        def items = wrappers.collect { it.getDataItem()}
        def chart = new CombinationChart(this, wrappers)
        def queryName = "Query" + (queryMap.size() + 1)
        def query = new Query(this, queryName, items)
        queryMap << [(queryName): query]
        chart.queryName = queryName
        chart.template = new CombinationChartTemplate(chart)
        chart
    }


    def v2BubbleChart(DataItemWrapper... wrappers) {
        def items = wrappers.collect {it.getDataItem()}
        def bubble = new V2BubbleChart(this, wrappers)
        def queryName = "Query" + (queryMap.size() + 1)
        def query = new Query(this, queryName, items)
        queryMap << [(queryName): query]
        bubble.queryName = queryName
        bubble.template = new V2BubbleChartTemplate(bubble)
        bubble
    }


    def DataItem dataItem(subjectName, closure = null) {
        def item = new DataItem(subjectName, this)
        if (closure != null) {
            closure.delegate = item
            closure.call()
        }
        dataItemList.add(item)
        return item
    }

    def List list2(DataItemWrapper... wrappers) {
        def items = wrappers.collect {it.getDataItem()}
        def list = new List(this, wrappers)
        def queryName = "Query" + (queryMap.size() + 1)
        def query = new Query(this, queryName, items)
        queryMap << [(queryName): query]
//        elementList.add(list)
        list.queryName = queryName
        list.template = new ListTemplate(list)
//        println list
        list
    }

//    def layout(closure = null, layoutType = null, Object... elements) {
//        this.elementList = elements
//    }

//    def Stri    ng generateSpec() {
////        desc()
//        def content = desc()
//        def elementName
//        content.each {
//            elementName = newName()
//            elementMap << [elementName: it]
//        }
//        builder.report(xmlns: "http://developer.cognos.com/schemas/report/7.0/", useStyleVersion: "10", expressionLocale: "en-us") {
//            generateModelPath()
//            generateDrillBehaviour()
//            generateQueries()
//            generateLayout()
//            generateXmlAttributes()
////            generateClassStyles()
//        }
//        return writer.toString()
//    }
//
//    def getSpecNode() {
//        def content = desc()
//        def elementName
//        content.each {
////            elementList << it
//            elementName = newName()
//            elementMap << [elementName: it]
//
//        }
//
//        def rootNode = builder.report(xmlns: "http://developer.cognos.com/schemas/report/7.0/", useStyleVersion: "10", expressionLocale: "en-us")
//        builder.setParent(rootNode, generateModelPath())
//        builder.setParent(rootNode, generateDrillBehaviour())
//        builder.setParent(rootNode, generateQueries())
//        builder.setParent(rootNode, generateLayout())
//        builder.setParent(rootNode, generateXmlAttributes())
//    }

    def getSpecNode() {
        def content = desc()
        if (content instanceof Collection) {
            content.each {
                def elementName = newName()
                elementMap << [(elementName): it]
            }
        } else {
            elementMap << [(newName()):content]
        }
        def reportTemplate = new ReportTemplate(this)
        root = reportTemplate.getTemplateNode()
        root
    }

    def String getSpec() {
        println "getSpec function"
        def generated = XmlUtil.serialize(getSpecNode())
        generated.readLines()[1..-1].join('\n') //remove the first line: xml declaration.
    }

    def filter(exp) {
        filterList << new Filter(exp, this)
    }



//    def static load(reportPath) {
//        CMServiceUtil cmUtil = new CMServiceUtil()
//        def reportName =
//        def reportSearchpath =
//        def reportSpec = cmUtil.getReportSpec(reportName, reportSearchpath)
//
//    }

//    def standaloneHtml() {
//
//    }
//
//    def html(){
//
//    }
//
//    def xml(){
//
//    }
//
//    def saveAs(){
//
//    }


    def update(elementName, closure) {
        def iterator = elementMap.keySet().iterator()
        def updatedElement

        while (iterator.hasNext()) {
            def key = iterator.next()
//            println(key)
            if (key == elementName) {
                def element = elementMap.get(key)
                iterator.remove()
                updatedElement = closure.call(element)
//                println(updatedElement)
            }
        }
        if (updatedElement != null) {
            updateSpecDom(elementName, updatedElement)
            elementMap.put(elementName, updatedElement)
        } else {
            throw new RuntimeException("Element ${elementName} not found")
        }
//        println(elementMap)
    }

    def updateSpecDom(elementName, newElement) {
        for (node in rootNode.depthFirst()) {
            if (node.@name == elementName) {
                node.replaceNode {
                    def sw = new StringWriter()
                    def builder = new MarkupBuilder(sw)
//                    println("hi")
                    newElement.generateSpec(builder)
                    String spec = sw.toString()
                    println("[updateSpecDom] $spec")
                    def elementNode = new XmlSlurper(false, false).parseText(spec)

                    mkp.yield(elementNode)
                }
            }
        }
    }

    def updateSpec2(name, updatedElement) {
        String oldSpec = getSpecOfElement(name)
//        println("replacing $element with ${updatedElement.generateSpec(new MarkupBuilder(new StringWriter()))}")
        println("[oldSpec] $oldSpec")
        def wr = new StringWriter()
        updatedElement.generateSpec(new MarkupBuilder(wr))
        String newSpec = wr.toString()
//        println("[newspec] ${wr.toString()}")
        println("[updateSpecDom] found oldSpec? ${specification.contains(oldSpec)}")
        specification = specification.replace(oldSpec, newSpec)
    }

    String getSpecOfElement(elementName) {
        String spec
        def contents = rootNode.layouts[0].layout[0].reportPages[0].page[0].pageBody[0].contents[0]
        for (it in contents.depthFirst()) {
//        contents.depthFirst().each {
//            println("${it.name()} ${it.@name}")
            if (it.@name == elementName) {
                spec = getFormattedXml(it)
//                println("[getSpecOfElement] ${spec}")
//                println("[getSpecOfElement] return $spec")
                break
            }
        }
        return spec
    }

    def getLayoutsSpecStr() {
        if (layoutsSpecStr) {
            generateSpec()
        }
    }
}
